<template>
  <view class="mxp-bubble" :style="[cusStyle]" :class="cusClass" @touchmove.stop.passive="preventClick">
    <mxp-translate
      v-if="mask && data.showTooltip"
      :autoShow="true"
      :cusStyle="{ ...data.maskStyle, zIndex }"
      :duration="0"
      mode="fade"
      @click="onMaskClose"
    ></mxp-translate>
    <mxp-translate
      v-if="data.showTooltip"
      key="mxp-translate-2"
      ref="mxpTranslateRef"
      :autoShow="false"
      :cusStyle="{ opacity: 0, ...data.contentPopupStyle, zIndex: zIndex + 1 }"
      :mode="data.mode"
      :duration="data.duration"
      :transform-origin="data.transformOrigin"
      @animationStart="onAnimationStart"
      @animationEnd="onAnimationEnd"
    >
      <!-- 弹出的内容 -->
      <view :id="data.contentId" class="content-view" :style="[{ background }, bubbleStyle]" @touchmove.stop.passive="preventClick">
        <view v-if="data.enoughShow" class="arrow" :style="[{ background }, data.arrowStyle]"></view>
        <view style="display: flex; align-items: flex-start; justify-content: flex-start" :style="{ minHeight: '10px' }">
          <slot>
            <view style="width: 60px; height: 60px"></view>
          </slot>
        </view>
      </view>
    </mxp-translate>
    <!-- 按钮 -->
    <view :id="data.buttonId" class="text-view" @click="show()">
      <slot name="button">
        <text class="text" :userSelect="false" :selectable="false" :style="[{ color: buttonTextColor, fontSize: buttonTextSize }, buttonTextStyle]">
          {{ buttonText }}
        </text>
      </slot>
    </view>
  </view>
</template>

<script lang="ts" setup>
  import MxpTranslate from '../mxp-translate/mxp-translate.vue';
  import { type CSSProperties, getCurrentInstance, nextTick, reactive, ref } from 'vue';
  import type { $AllColorStr, $AnimationMode, $SizeUnitStr } from '../mxp-tool/types/interface';
  import { getDomSize, getXPNumber } from '../mxp-tool/utils/dom';
  import { getOnlyID } from '../mxp-tool/utils/common';
  import { getPlacement } from './placement';
  import mxpGetSysInfoStore from '../mxp-tool/store/mxp-store-sys-info';
  export type $BubbleProps = {
    /** 按钮文本 */
    buttonText?: number | string;
    /** 按钮文本大小 */
    buttonTextSize?: $SizeUnitStr;
    /** 按钮文本颜色 */
    buttonTextColor?: $AllColorStr;
    /** 按钮文本颜色 */
    buttonTextStyle?: CSSProperties;
    /** 弹出内容的样式 */
    bubbleStyle?: CSSProperties;
    /** 弹出提示框时，文本的背景色 */
    background?: $AllColorStr;
    /** 弹出提示框的宽度 */
    width?: $SizeUnitStr;
    /** 弹出提示框的高度 */
    height?: $SizeUnitStr;
    /** 弹出提示的方向， 'auto' | 'bottom' | 'left' | 'right' | 'top' */
    direction?: 'auto' | 'bottom' | 'left' | 'right' | 'top';
    /** 弹出提示的z-index，nvue无效 */
    zIndex?: number;
    /** 是否显示透明遮罩以防止触摸穿透 */
    mask?: boolean;
    /** 是否可以点击遮罩关闭 */
    maskClose?: boolean;
    /** 自定义的样式属性 */
    cusStyle?: CSSProperties;
    /** 自定义类名 */
    cusClass?: string;
  };

  defineOptions({
    name: 'mxp-bubble',
  });
  const props = withDefaults(defineProps<$BubbleProps>(), {
    buttonText: '',
    buttonTextSize: '14px',
    buttonTextColor: '#606266',
    buttonTextStyle: () => ({}),
    bubbleStyle: () => ({}),
    background: '#ffffff',
    direction: 'auto',
    width: undefined,
    height: undefined,
    zIndex: 999999,
    mask: true,
    maskClose: true,
    cusStyle: () => ({}),
    cusClass: '',
  });
  defineSlots<{
    /** 主体内容 */
    default?: () => void;
    /** 底部内容 */
    button?: () => void;
  }>();
  const emits = defineEmits<{
    (e: 'show'): void;
    (e: 'hide', type: 'mask' | 'rest'): void;
    (e: 'update:modelValue', state: boolean): void;
    /** 动画开始 */
    (e: 'animationStart', type: 'hide-animation' | 'show-animation'): void;
    /** 动画完成 */
    (e: 'animationEnd', type: 'hide-animation' | 'show-animation'): void;
  }>();

  const mxpTranslateRef = ref<InstanceType<typeof MxpTranslate> | null>(null);

  const instance = getCurrentInstance();

  const data = reactive({
    enoughShow: false,
    contentId: getOnlyID(`mxp-tooltip_content`),
    buttonId: getOnlyID(`mxp-tooltip_button`),
    transformOrigin: 'center center',
    mode: 'fade' as $AnimationMode,
    showTooltip: false, // 是否展示气泡
    duration: 0,
    loop: 0,
    arrowStyle: { left: '10px', top: '10px' } as CSSProperties,
    contentPopupStyle: { opacity: 0, zIndex: props.zIndex + 1, left: `${50}px`, position: 'fixed' } as CSSProperties,
    maskStyle: {} as CSSProperties,
  });

  /**
   * 阻止传递
   */
  function preventClick() {
    // 阻止传递
  }

  /** 计算位置 */
  function getDirection(sys: UniApp.GetSystemInfoResult, contentRect: { width: number; height: number }, buttonRect: UniApp.NodeInfo) {
    const rec = getPlacement(sys, contentRect, buttonRect, props.direction);
    data.enoughShow = rec.enoughShow;
    data.arrowStyle = { left: rec.arrowsLeft, top: rec.arrowsTop };
    data.contentPopupStyle = { opacity: 0, left: rec.bubbleLeft, top: rec.bubbleTop, position: 'fixed', zIndex: props.zIndex + 1 };
    data.mode = rec.mode;
  }

  /**
   * 显示
   * 支持手动给定 按钮 位置信息 从而无需 <slot #button></slot> 适合用于一个页面有多个bubble，的情况
   */
  async function show(_rect?: UniApp.NodeInfo) {
    const rect = _rect || (await getDomSize(`#${data.buttonId}`, instance));

    const sys = mxpGetSysInfoStore().getSysInfo();
    data.mode = 'fade' as $AnimationMode;
    data.showTooltip = false; // 是否展示气泡
    data.duration = 0;
    data.contentPopupStyle.opacity = 0;
    data.contentPopupStyle = { opacity: 0, zIndex: props.zIndex + 1, left: `${50}px`, position: 'fixed' };
    data.contentPopupStyle.opacity = 0;
    data.maskStyle = {
      height: `${sys.value.windowHeight}px`,
      width: `${sys.value.windowWidth}px`,
      bottom: '0px',
      top: `${sys.value.windowTop}px`,
      left: '0px',
      zIndex: props.zIndex,
      position: 'fixed',
      backgroundColor: 'rgba(0, 0, 0, 0)',
    };

    nextTick(() => {
      data.showTooltip = true;
      // 先emits后获取高度，否则首次获取可能不正常
      emits('update:modelValue', true);
      emits('show');
      setTimeout(() => {
        const width = getXPNumber(props.width || '0px', 0);
        const height = getXPNumber(props.height || '0px', 0);
        if (width && height) {
          getDirection(sys.value, { width, height }, rect);
          data.duration = 300;
          mxpTranslateRef.value?.show();
        } else {
          timeoutGetSize(sys.value, rect);
        }
      }, 10);
    });
  }
  /** 循环获取高度 */
  function timeoutGetSize(sys: UniApp.GetSystemInfoResult, rect: UniApp.NodeInfo) {
    setTimeout(async () => {
      data.loop++;
      const { width, height } = await getDomSize(`#${data.contentId}`, instance);
      if ((width && height) || data.loop > 5) {
        data.loop = 0;
        getDirection(sys, { width: width || 0, height: height || 0 }, rect);
        data.duration = 300;
        data.contentPopupStyle.opacity = 1;
        setTimeout(() => {
          mxpTranslateRef.value?.show();
        }, 0);
      } else {
        timeoutGetSize(sys, rect);
      }
    }, 30);
  }

  /** 关闭完成 */
  function onAnimationEnd(type: 'hide-animation' | 'show-animation') {
    emits('animationEnd', type);
    if (type === 'hide-animation') {
      data.showTooltip = false;
    }
  }
  /** 开始动画 */
  function onAnimationStart(type: 'hide-animation' | 'show-animation') {
    emits('animationStart', type);
  }

  /**
   * 点击遮罩
   */
  function onMaskClose() {
    props.maskClose && hide('mask');
  }

  /** 隐藏 */
  function hide(type: 'mask' | 'rest' = 'rest') {
    emits('hide', type);
    emits('update:modelValue', false);
    mxpTranslateRef.value?.hide();
  }

  defineExpose({
    /**
     * 显示
     * 支持手动给定 按钮 位置信息 从而无需 <slot #button></slot> 适合用于一个页面有多个bubble，的情况
     */
    show,
    /** 隐藏 */
    hide,
  });
</script>

<style lang="scss" scoped>
  .mxp-bubble {
    display: flex;
    flex-direction: row;

    .text-view {
      display: flex;
      flex-direction: row;
    }

    .content-view {
      border-radius: 8px;
    }
  }

  .arrow {
    position: absolute;
    width: 12px;
    height: 12px;
    border-radius: 2px;
    transform: rotate(45deg);
    transform-origin: center center;
  }
</style>
